package com.aidisp.dao.impl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.aidisp.dao.BaseDao;
import com.aidisp.dao.PageResult;
import com.aidisp.model.admin.User;
@Repository
public abstract class BaseDaoImpl implements BaseDao {
	@Autowired
	private SessionFactory sessionFactory;


	@Override
	public <T> void save(T t) {
		Session session = this.sessionFactory.getCurrentSession();
		session.save(t);
	}

	@Override
	public <T> void update(T t) {
		Session session = this.sessionFactory.getCurrentSession();
		session.update(t);
	}

	@Override
	public <T> void delete(T t) {
		Session session = this.sessionFactory.getCurrentSession();
		session.delete(t);
		
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getById(Class<T> clazz, Serializable id) {
		Session session = this.sessionFactory.getCurrentSession();
		Object obj = session.get(clazz, id);
		return (T)obj;
	}
	@SuppressWarnings("unchecked")
	@Override
	public <T> T loadById(Class<T> clazz, Serializable id) {
		Session session = this.sessionFactory.getCurrentSession();
		Object obj = session.load(clazz, id);
		return (T)obj;
	}
	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> findAllEntity(Class<T> clazz) {
		Session session = this.sessionFactory.getCurrentSession();
		Query query = session.createQuery("from "+clazz.getName());
		return query.list();
	}
	@Override
	public <T> void batchSave(Collection<T> collection) {
		Session session = this.sessionFactory.getCurrentSession();
		for(T t:collection){
			session.save(t);
		}
	}

	@Override
	public <T> void batchUpdate(Collection<T> collection) {
		Session session = this.sessionFactory.getCurrentSession();
		for(T t:collection){
			session.update(t);
		}
	}

	@Override
	public <T> void batchDelete(Collection<T> collection) {
		Session session = this.sessionFactory.getCurrentSession();
		for(T t:collection){
			session.delete(t);
		}
	}
	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> executeQueryByHQL(String hql,Class<T> clazz,Object...paramValues){
		Session session = this.sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		for(int i=0;i<paramValues.length;i++){
			query.setParameter(i, paramValues[i]);
		}
		return (List<T>)query.list();
		
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> executeQueryByNamedQuery(String namedQuery,Class<T> clazz, Object... paramValues) {
		Session session = this.sessionFactory.getCurrentSession();
		Query query = session.getNamedQuery(namedQuery);
		for(int i=0;i<paramValues.length;i++){
			query.setParameter(i, paramValues[i]);
		}
		return (List<T>)query.list();
	}
	
	@Override
	public <T> PageResult<T> getPageResultByNamedQuery(String namedQuery, int pageSize,int currentPageNo, Object... paramValues) {
		Query query = this.sessionFactory.getCurrentSession().getNamedQuery(namedQuery);
		for(int i=0;i<paramValues.length;i++){
			query.setParameter(i, paramValues[i]);
		}
		return buildPageResult(query, pageSize, currentPageNo,paramValues);
	}
	@Override
	public <T> PageResult<T> getPageResultByHQL(String hql, int pageSize,
			int currentPageNo, Object... paramValues) {
		Query query = this.sessionFactory.getCurrentSession().createQuery(hql);
		for(int i=0;i<paramValues.length;i++){
			query.setParameter(i, paramValues[i]);
		}
		return buildPageResult(query, pageSize, currentPageNo,paramValues);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T> List<T> findAllEntityJoinFetch(Class<T> clazz,List<Class> clazzs) {
		Session session = this.sessionFactory.getCurrentSession();
		String entityName = clazz.getName();
		String entitySimplName = clazz.getSimpleName().toLowerCase();
		Field[] fields = clazz.getDeclaredFields();
		List<Field> joinField = new ArrayList<Field>();
		for(Class<?> c:clazzs){
			for(Field field:fields){
				Type fieldType = field.getGenericType();
				if(fieldType.equals(c)){
					joinField.add(field);
				}else{
					if(fieldType instanceof ParameterizedType){
						ParameterizedType parameterizedType = (ParameterizedType)fieldType;
						Class<?> genericityClass = (Class<?>)parameterizedType.getActualTypeArguments()[0];
						if(genericityClass.equals(c)){
							joinField.add(field);
						}
					}
				}
			}
		}
		StringBuffer hql = new StringBuffer("from " + entityName + " " + entitySimplName);
		for(Field field:joinField){
			hql.append(" inner join fetch "+entitySimplName+"."+field.getName());
		}
		
//		String hql = "from " + entityName + " " + entitySimplName + " inner join fetch "+entitySimplName+".groups";
		Query query = session.createQuery(hql.toString());
		return (List<T>)query.list();
	}

	private long getEntityCount(String hql,Object...paramValues){
		Query query = this.sessionFactory.getCurrentSession().createQuery("select count(*) "+hql);
		for(int i=0;i<paramValues.length;i++){
			query.setParameter(i, paramValues[i]);
		}
		long count = (Long)query.uniqueResult();
		return count;
	}
	
	
	@SuppressWarnings("unchecked")
	private <T> PageResult<T> buildPageResult(Query query,int pageSize,int currentPageNo,Object...paramValues){
		long entityCount = (Long)getEntityCount(query.getQueryString(),paramValues);
		//总页数
		int pageCount = (int)(entityCount%pageSize==0?entityCount/pageSize:entityCount/pageSize+1);
		//当前页的记录数
		int actualSize = 0;
		if(currentPageNo>=pageCount){
			currentPageNo = pageCount;
			actualSize = (int)(entityCount-(currentPageNo-1)*pageSize);
		}else{
			actualSize = pageSize;
		}
		//分页
		query.setFirstResult((currentPageNo-1)*pageSize);
		query.setMaxResults(actualSize);
		
		class BasePageResult implements PageResult<T> {
			private long resultCount;
			private int firstPageNo=1;
			private int previousPageNo;
			private int currentPageNo;
			private int nextPageNo;
			private int lastPageNo;
			private int currentResultCount;
			private List<T> result;
			public BasePageResult(long resultCount,int lastPageNo,int currentPageNo,int currentResultCount,List<T> result){
				this.resultCount = resultCount;
				this.lastPageNo = lastPageNo;
				this.result = result;
				this.currentResultCount = currentResultCount;
				if(this.lastPageNo<=1){
					this.currentPageNo = 1;
					this.previousPageNo = 1;
					this.nextPageNo = 1;
					this.lastPageNo = 1;
				}else if(currentPageNo>=lastPageNo){
					this.currentPageNo = lastPageNo;
					this.previousPageNo = lastPageNo - 1;
					this.nextPageNo = lastPageNo;
				}else if(currentPageNo<=1){
					this.currentPageNo = 1;
					this.nextPageNo = 2;
					this.previousPageNo = 1;
				}else{
					this.currentPageNo = currentPageNo;
					this.nextPageNo = currentPageNo + 1;
					this.previousPageNo = currentPageNo - 1;
				}
			}
			
			@Override
			public long getResultCount() {
				return resultCount;
			}

			@Override
			public int getFirstPageNo() {
				return firstPageNo;
			}

			@Override
			public int getPreviousPageNo() {
				return previousPageNo;
			}

			@Override
			public int getCurrentPageNo() {
				return currentPageNo;
			}

			@Override
			public int getNextPageNo() {
				return nextPageNo;
			}

			@Override
			public int getLastPageNo() {
				return lastPageNo;
			}

			@Override
			public int getCurrentResultCount() {
				return currentResultCount;
			}

			@Override
			public List<T> getResult() {
				return this.result;
			}

			@Override
			public void setResult(List<T> result) {
				this.result = result;
			}

		}
		return new BasePageResult(entityCount, pageCount, currentPageNo, actualSize, (List<T>)query.list());
	}
	@Override
	public void clearSession(Session session) {
		session.clear();
		
	}

	@Override
	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}
	public static void main(String[] args) throws Exception {
//		Field field = User.class.getDeclaredField("groups");
//		System.out.println(field.getName());
//		System.out.println(field.getGenericType().getClass());
//		Type genericType = field.getGenericType();
//		if(genericType instanceof ParameterizedType){
//			ParameterizedType type = (ParameterizedType)field.getGenericType();
//			Type[] types = type.getActualTypeArguments();
//			for(Type fieldArgType : types){
//		        Class<?> argClass = (Class<?>) fieldArgType;
//		        System.out.println(argClass.equals(Group.class));
//		    }
//		}
		Field[] fields = User.class.getDeclaredFields();
		
		for(Field field:fields){
			System.out.println(field.getName()+":"+field.getGenericType().equals(String.class));
		}
		
	}
}
